--[[
Name : Queeg
Authors : Drool and Livid
Website : www.serenitygaming.org
Description : Queue management and information addon for raiding guilds
]]

-- declard color codes for console messages
local RED     = "|cffff0000"
local GREEN   = "|cff00ff00"
local YELLOW  = "|cffffff00"
local GREY    = "|cff777777"

local L = AceLibrary("AceLocale-2.2"):new("Queeg")

Queeg = AceLibrary("AceAddon-2.0"):new("FuBarPlugin-2.0",
	"AceConsole-2.0", "AceEvent-2.0", "AceDB-2.0", "WhoLib-1.0") -- "AceComm-2.0")
local tablet = AceLibrary("Tablet-2.0")
local BC = AceLibrary("Babble-Class-2.2")
local RC = AceLibrary("RollCall-1.0")

Queeg:RegisterDB("QueegDB")
Queeg:RegisterDefaults("profile", {
	currentQueue = {},
	playerCache = {},
} )

Queeg.debug = false

--Queeg:SetCommPrefix("Queeg")

function Queeg:OnInitialize()
	-- sets our fubar icon and right-click menu title
	self.hasIcon = true
	self:SetIcon("Interface\\Icons\\INV_Scroll_05")
	self.title = "Queeg"

	self.clickableTooltip = true
	self.hasText = true

	self.currentQueue = self.db.profile.currentQueue
	self.playerCache = self.db.profile.playerCache

	-- declare this popup once only
	-- we have to pass an extra parameter for StaticPopup_Show to insert name into 'text', plus set up parameter for OnAccept function
	StaticPopupDialogs["CONFIRM_PLAYERREMOVAL"] = {
		text = L["Are you sure you want to remove %s ?"],
		button1 = L["Yes"], button2 = L["No"],
		OnAccept = function(names)
			local name, onlineAs = names[1], names[2]
			if onlineAs then
				Queeg:SendQueegMessage(L["You have been removed from the Queue."], "WHISPER", onlineAs)
			end
			self.currentQueue[name] = nil
			Queeg:Update()
		end,
		timeout = 0, whileDead = 1, hideOnEscape = 1
	}
end

function Queeg:OnEnable()
	-- called when the addon is enabled
	--self:RegisterComm(self.commPrefix, "GUILD", "ReceiveMessage")
	self:RegisterEvent("CHAT_MSG_WHISPER")
	self:RegisterEvent("CHAT_MSG_SYSTEM")

	if IsInGuild() then
		self:RegisterBucketEvent("GUILD_ROSTER_UPDATE", 1)
		GuildRoster()
	end
	
	self:Update()
end

function Queeg:RollCall10_MemberAdded(name)
	if not self.playerCache[name] then
		self:DebugMessage(name.." was added to the player cache")
		self.playerCache[name] = { totalTimeQueued = 0, }
	end
end

function Queeg:RollCall10_MemberRemoved(name)
	if self.playerCache[name] then
		self:DebugMessage(name.." was removed from the player cache")
		self.playerCache[name] = nil
	end
	
	self.currentQueue[name] = nil
end

function Queeg:GUILD_ROSTER_UPDATE()
	self:UpdatePlayerCache()
	self:UnregisterBucketEvent("GUILD_ROSTER_UPDATE")
	self:RegisterEvent("RollCall10_MemberAdded")
	self:RegisterEvent("RollCall10_MemberRemoved")
end

local unitJoinedRaid = '^'..ERR_RAID_MEMBER_ADDED_S:gsub("%%s", "(.-)")..'$'
local unitJoinedParty = '^'..ERR_JOINED_GROUP_S:gsub("%%s", "(.-)")..'$'
local unitWentOffline = '^'..ERR_FRIEND_OFFLINE_S:gsub("%%s", "(.-)")..'$'

function Queeg:CHAT_MSG_SYSTEM(msg)
	local name = select(3, msg:find(unitJoinedRaid))
	--local name = select(3, msg:find(unitWentOffline))
	
	if not name then
		name = select(3, msg:find(unitJoinedParty))
	end
	

	if name then
		self.currentQueue[name] = nil
		self:Update()
	end
	
	if not name then
		name = select(3, msg:find(unitWentOffline))
	end
	
	--Code to remove someone from the Queue when they are detected to have gone offline via system messages ( %s has gone offline )
	if name then
		if self.currentQueue[name] then
			self.currentQueue[name].lastOnline = time()
			Queeg:OnTextUpdate()
		end
	end
end

function Queeg:CHAT_MSG_WHISPER(msg, sender)
	local Args = self:GetArgs(msg, " ")
	if #(Args) < 1 then return; end
	if string.lower(Args[1]) ~= "queue" then return; end
	local command; if Args[2] then command = string.lower(Args[2]); end

	--Inactive Code below can be used to locate position of keywords ( i.e we can check that a keyword was the first word rather than it being mentioned somewhere else in the text)
	--local startPos, endPos, firstWord, restOfString = string.find( msg, "(%w+)[%s%p]*(.*)");

	if command == nil then
		Queeg:SendQueegMessage(L["Whisper the word QUEUE and one of the following keywords, ADD, REMOVE, LIST, ALT, ALTS or QUEUE HELP and a keyword for more info"], "WHISPER", sender)
		return
		
	elseif command == "cmds" then
		Queeg:SendQueegMessage(L["Whisper the word QUEUE and one of the following keywords, ADD, REMOVE, LIST, ALT, ALTS or QUEUE HELP and a keyword for more info"], "WHISPER", sender)

	elseif command == "alts" then
		if Args[3] == nil then
			Queeg:listAlts(sender)
		elseif Args[3] == "clear" then
			self.playerCache[sender].alts = nil
			Queeg:SendQueegMessage(L["Your registered alts have been cleared, use 'queue alt altname' to re-register an alt."], "WHISPER", sender)
		end

    elseif command == "add" then 
	 
	        if self.currentQueue[sender] then 
	            Queeg:SendQueegMessage(L["You are already queued."], "WHISPER", sender) 
	            self:DebugMessage(sender.." asks for queue, already in it") 
	            return 
	        end 
	 
	        local found 
	 
	        -- check if sender is an alt... 
	        for i,v in pairs(self.playerCache) do 
	            if self.playerCache[i].alts and self.playerCache[i].alts[sender] then 
	                found = i 
	                break 
	            end 
	        end 
	 
	        if not found and not self.playerCache[sender] then 
	            Queeg:SendQueegMessage(L["You are not permitted to join this guild's Queue."], "WHISPER", sender) 
	            return 
	        elseif found then 
	            self.currentQueue[found] = { timeQueued = time(), } 
	        else 
	            self.currentQueue[sender] = { timeQueued = time(), } 
	        end 
	 
	        Queeg:Update() 
	 
	        -- if the whisper comes from ourself (debug purposed), then don't respond 
	        if sender ~= UnitName("player") then 
	            Queeg:SendQueegMessage(L["You have been added to the queue - whisper the command 'queue cmds' for a list of available commands."], "WHISPER", sender) 
	        end 
	 
	        DEFAULT_CHAT_FRAME:AddMessage(string.format(L["%s was added to the queue"], sender), 1.0, 0.0, 0.0) 
			
	elseif command == "remove" then
		if self.currentQueue[sender] then
			self.currentQueue[sender] = nil
			Queeg:SendQueegMessage(L["You have been removed from the Queue."], "WHISPER", sender)
			Queeg:Update()
		else
			Queeg:SendQueegMessage(L["You are not currently queued."], "WHISPER", sender)
		end

	elseif command =="help" then
		if (Args[3]) =="add" then
			Queeg:SendQueegMessage(L["Whisper QUEUE ADD to add yourself to the Queue"], "WHISPER", sender)
		elseif (Args[3]) =="remove" then
			Queeg:SendQueegMessage(L["Whisper QUEUE REMOVE to remove yourself from the Queue"], "WHISPER", sender)
		elseif (Args[3]) =="list" then
			Queeg:SendQueegMessage(L["Whisper QUEUE LIST to get the current Queue"], "WHISPER", sender)
		elseif (Args[3]) =="alt" then
			Queeg:SendQueegMessage(L["Whisper QUEUE ALT <alt name> to add an alt to your current character, use QUEUE ALT REMOVE <alt name> to remove a specific alt."], "WHISPER", sender)
		elseif (Args[3]) =="alts" then
			Queeg:SendQueegMessage(L["Whisper QUEUE ALTS to request a list of all your currently registered alts, use QUEUE ALTS CLEAR to delete all your registered alts."], "WHISPER", sender)
		end

	elseif command =="alt" then
		if (Args[3]) == "remove" and (Args[4]) ~= nil then
			local alt_name = string.upper(string.sub(Args[4], 1, 1))..string.lower(string.sub(Args[4], 2))

			if self.playerCache[sender].alts and self.playerCache[sender].alts[alt_name] then
				self.playerCache[sender].alts[alt_name] = nil
				Queeg:SendQueegMessage(string.format(L["%s was removed as an ALT from %s."], alt_name, sender), "WHISPER", sender)
			elseif not self.playerCache[sender].alts[alt_name] then
				Queeg:SendQueegMessage(string.format(L["Unable to remove %s as an alt for %s as it is not registered to your character"], alt_name, sender), "WHISPER", sender)
			elseif not self.playerCache[sender] then
				Queeg:SendQueegMessage((L["You are not permitted to remove characters from this queue"]), "WHISPER", sender)
			end

		elseif not Args[3] then
			Queeg:listAlts(sender)

		else
			local alt_name = Args[3]
			if not alt_name then return; end
			if #(alt_name) <= 2 then return; end

			alt_name = string.upper(string.sub(alt_name, 1, 1))..string.lower(string.sub(alt_name, 2))

			if self.playerCache[sender].alts and self.playerCache[sender].alts[alt_name] then
				Queeg:SendQueegMessage(string.format(L["You cannot register %s as an alt for %s as it is already registered to it."], alt_name, sender), "WHISPER", sender)
			else
				if not self.playerCache[sender].alts then self.playerCache[sender].alts = { }; end
				self.playerCache[sender].alts[alt_name] = true
				Queeg:SendQueegMessage(string.format(L["%s was added as an alt for your current character."], alt_name), "WHISPER", sender)
			end
		end

	elseif command == "list" then
		self:AnnounceCurrentQueue("WHISPER", sender)

	end

end

function Queeg:OnTooltipUpdate()
	-- sets the contents of the tooltip. this is automatically called by fubar when needed
	--GuildRoster()

	local cat = tablet:AddCategory(
		'text', "Queued players",
		'text2', "Status",
		'columns', 2
	)

	for name, data in pairs(self.currentQueue) do
		local classColourR, classColourG, classColourB
		local isOnline, onlineAs
		local timer_text
		local displayname = name
		local temp_non_guilded = { }
		
		if IsInGuild() then
			isOnline = RC:IsMemberOnline(name)

			if not isOnline and self.playerCache[name].alts then
				-- now we need to check the list of alts to see if they're online
				for altname in pairs(self.playerCache[name].alts) do
					if RC:HasMember(altname) then
						if RC:IsMemberOnline(altname) then
							local altcolor = RC:GetClassHexColor(altname)
							isOnline = true
							onlineAs = altname
							displayname = name..
								string.format(L["|r |cffeda55f(|cffccccccon alt|r |cff%s%s|r|cffeda55f)|r"], altcolor, altname)
							break
						end
					else
						tinsert(temp_non_guilded, altname)
					end
				end

				if not isOnline then
					for i,v in pairs(temp_non_guilded) do
						local user = self:UserInfo(v, { callback = 'WhoCallback', timeout = 0.75 } )
						if user and user.Online then
							local altcolor = BC:GetHexColor(user.Class)
							isOnline = true
							onlineAs = user.Name
							displayname = name..
								string.format(L["|r |cffeda55f(|cffccccccon alt|r |cff%s%s|r|cffeda55f)|r"], altcolor, onlineAs)
							break
						end
					end
				end
			end

			if isOnline then
				displayname = "|cff"..RC:GetClassHexColor(name)..displayname
				timer_text = GREEN..date("%H:%M", data.timeQueued)
			else
				displayname = GREY..displayname
				local lastOnline = date("%H:%M", data.lastOnline)

				if RC:GetSecondsOffline(name) > 300 then
					timer_text = RED..L["Offline"]
				else
					timer_text = YELLOW..L["Offline"]
				end
				
				if data.lastOnline then
					timer_text = RED..lastOnline
				end
			end

		else
			displayname = GREY..displayname
			timer_text = GREY.."Unknown"
		end

		cat:AddLine(
			'justifyH', "CENTER",
			'text', displayname,
			'func', function()
				if isOnline then
					if IsShiftKeyDown() then
						Queeg:RemovePlayer(name, onlineAs)
					else
						-- for now, onlineAs is set only when we have an online alt, nil otherwise
						if onlineAs then
							Queeg:SendQueegMessage(L["Please log your main as you have been invited to the raid"], "WHISPER", onlineAs)
							DEFAULT_CHAT_FRAME:AddMessage(string.format(L["%s was notified they are required for the raid."],displayname), 1.0, 0.0, 0.0)
						else
							Queeg:PlayerInvite(name)
						end
					end
				else
					Queeg:RemovePlayer(name)
				end
			end,
			'text2', timer_text
		)
	end

	tablet:SetHint(L["|cffeda55fClick|r to invite a player or to remove offline players. |cffeda55fShift-Click|r to remove a player."])
end

-- NOT using an AceOptions data table
local dewdrop = AceLibrary("Dewdrop-2.0")
function Queeg:OnMenuRequest(level, value, inTooltip)
	-- sets the fubar right-click menu structure. this is called automatically
	--if inTooltip then return end

	dewdrop:AddLine(
		'text', L["Advertise queue open"],
		'func', function()
			Queeg:AdvertiseQueueOpen()
		end
	)

	dewdrop:AddLine(
		'text', L["Clear current queue"],
		'func', function()
			if next(self.currentQueue) then
				dewdrop:Close(1)
				StaticPopupDialogs["CONFIRM_QUEUECLEAR"] = {
					text = L["Are you sure you want to clear the entire queue?"],
					button1 = L["Yes"], button2 = L["No"],
					OnAccept = function() Queeg:RemoveAll() end,
					timeout = 0, whileDead = 1, hideOnEscape = 1
				}
				StaticPopup_Show("CONFIRM_QUEUECLEAR")
			else
				DEFAULT_CHAT_FRAME:AddMessage(L["Nobody in queue"], 1.0, 0.0, 0.0)
			end

		end
	)

	dewdrop:AddLine(
		'text', "Announce queue in Guildchat",
		'func', function()
			Queeg:AnnounceCurrentQueue("GUILD")
		end
	)
	
	dewdrop:AddSeparator()
end

function Queeg:OnTextUpdate()
	local n = 0; for _ in pairs(self.currentQueue) do n = n + 1; end

	if n == 0 then
		self:SetText(L["Empty"])
	else
		self:SetText(L["Queued: "].."|cffffffff"..n.."|n")
	end
end

function Queeg:DebugMessage(msg)
	if self.debug then
		ChatFrame4:AddMessage("[Queeg]: "..msg)
	end
end

-- extract key/value from message
function Queeg:GetArgs(message, separator)
	local args = {}
	local i = 0

	-- search for seperators in the string and return the separated data
	for value in string.gmatch(message, "[^"..separator.."]+") do
		i = i + 1
		args[i] = value
	end

	return args
end

function Queeg:RemoveAll()
	if not next(self.currentQueue) then
		DEFAULT_CHAT_FRAME:AddMessage(L["Nobody in queue"], 1.0, 0.0, 0.0)
	else	
		for name, data in pairs(self.currentQueue) do
			self.currentQueue[name] = nil
			Queeg:Update()
		end
		DEFAULT_CHAT_FRAME:AddMessage(L["Queue was cleared - all players removed"], 1.0, 0.0, 0.0)
	end
end

function Queeg:AdvertiseQueueOpen()
	Queeg:SendQueegMessage(L["Whisper the command 'queue add' to join the queue."], "GUILD")
end

function Queeg:AnnounceCurrentQueue(channel, player)
	local queueList = ""

	for name, data in pairs(self.currentQueue) do
		queueList = queueList..name..", "
	end

	if queueList =="" then
		Queeg:SendQueegMessage("Queue is empty", channel, player)
	else
		queueList = string.sub(queueList, 1, -3) -- remove the last comma
		Queeg:SendQueegMessage(L["Players currently queued: "]..queueList, channel, player)
		Queeg:SendQueegMessage(L["Whisper the command 'queue cmds' for more commands."], channel, player)
	end
end

function Queeg:PlayerInvite(name)
	DEFAULT_CHAT_FRAME:AddMessage(string.format(L["%s was invited to the raid"], name), 1.0, 0.0, 0.0)
	Queeg:SendQueegMessage(L["You have been invited to join the raid."], "WHISPER", name)
	InviteUnit(name)
	--Queeg:Update()
end

function Queeg:RemovePlayer(name, onlineAs)
	if not onlineAs and RC:IsMemberOnline(name) then
		onlineAs = name
	end

	local dialog = StaticPopup_Show("CONFIRM_PLAYERREMOVAL", name)
	if dialog then dialog.data = { name, onlineAs }; end
end

function Queeg:UpdatePlayerCache()
	local numGuildMembers = GetNumGuildMembers(true)

	for i = 1, numGuildMembers, 1 do
		local name, rank, rankIndex, level, class, zone, note, officernote, online, status = GetGuildRosterInfo(i)

		if not self.playerCache[name] then
			--DEFAULT_CHAT_FRAME:AddMessage(string.format(L["%s was added to the player cache."], name), 1.0, 0.0, 0.0)
			self:DebugMessage(name.." was added to the player cache.")
			self.playerCache[name] = { totalTimeQueued = 0, }
		end
	end
end

function Queeg:OnClick()
	if not QueegFrame:IsVisible() then
		self:ShowQueueListFrame()
	else
		QueegFrame:Hide()
	end
end

function Queeg:ShowQueueListFrame()
	local queueList = ""

	for name, data in pairs(self.currentQueue) do
		queueList = queueList..name.."\n"
	end

	QueegFrameScrollText:SetText(queueList)
	QueegFrame:Show()
	QueegFrameScrollText:SetFocus()
	QueegFrameScrollText:HighlightText()
end

--[[function Queeg:ReceiveMessage(prefix, sender, distribution, action, arg1, arg2, arg3, ...)
	if action == "add" then
		self.currentQueue[sender] = { timeQueued = time(), }
		DEFAULT_CHAT_FRAME:AddMessage("Message Received", 1.0, 0.0, 0.0)
	else
		return
	end
end

function Queeg:testComm()
	local chit = "1"
	local chat = "2"
	local action ="add"
	self:SendCommMessage("GUILD", action, chit, chat)
end]]

function Queeg:WhoCallback(user, time)
	self:ScheduleEvent("QUEEG_UPDATE", self.Update, 1, self)
end

function Queeg:listAlts(sender)

	if not self.playerCache[sender].alts or not next(self.playerCache[sender].alts) then
		Queeg:SendQueegMessage(string.format(L["You do not have any alts registered for %s, register one by whispering queue alt altname."], sender), "WHISPER", sender)
	else
		local altList = ""

		for name in pairs(self.playerCache[sender].alts) do
			altList = altList..name..", "
		end

		alt = string.sub(altList, 1, -3) -- remove the last comma
		Queeg:SendQueegMessage(string.format(L["The following alts are registered to %s: "],sender)..altList, "WHISPER", sender)
	end

end

function Queeg:SendQueegMessage(message,channel,sender)
    SendChatMessage("[Qu]: "..message, channel, nil, sender)
end

local ORIG_ChatFrame_MessageEventHandler = ChatFrame_MessageEventHandler;
function ChatFrame_MessageEventHandler(event)

	if event == "CHAT_MSG_WHISPER" and string.lower(string.sub(arg1, 1, 5)) == "queue" then
		-- ignore it
	elseif event == "CHAT_MSG_WHISPER_INFORM" and string.sub(arg1, 1, 6) == "[Qu]: " then
		-- ignore it
	else
		ORIG_ChatFrame_MessageEventHandler(event)
	end

end
